using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Web;
using System.Web.Mvc;
using System.Web.Script.Serialization;
using System.Xml.Serialization;

namespace RestUpMVC
{
    public class XmlValueProvider : DictionaryValueProvider<object>
    {
        private static Dictionary<string, object> _xmlObjectDictionary;

        public XmlValueProvider(ControllerContext controllerContext) : base(GetXmlObjectDictionary(controllerContext), CultureInfo.CurrentCulture)
        {
            if (controllerContext == null) throw new ArgumentNullException("controllerContext");
        }

        private static Dictionary<string, object> GetXmlObjectDictionary(ControllerContext controllerContext)
        {
            object xmlData = GetDeserializedXml(controllerContext);
            if (xmlData == null)
            {
                return _xmlObjectDictionary;
            }

            AddToBackingStore(_xmlObjectDictionary, String.Empty, xmlData);
            return _xmlObjectDictionary;
        }

        private static void AddToBackingStore(Dictionary<string, object> backingStore, string prefix, object value)
        {
            { // dictionary?
                IDictionary<string, object> d = value as IDictionary<string, object>;
                if (d != null)
                {
                    foreach (var entry in d)
                    {
                        AddToBackingStore(backingStore, MakePropertyKey(prefix, entry.Key), entry.Value);
                    }
                    return;
                }
            }

            { // list?
                IList l = value as IList;
                if (l != null)
                {
                    for (int i = 0; i < l.Count; i++)
                    {
                        AddToBackingStore(backingStore, MakeArrayKey(prefix, i), l[i]);
                    }
                    return;
                }
            }

            // primitive
            backingStore[prefix] = value;
        }

        private static object GetDeserializedXml(ControllerContext controllerContext)
        {
            XmlSerializer serializer = new XmlSerializer(typeof(object));
            object xmlData;
            try
            {
                xmlData = serializer.Deserialize(controllerContext.HttpContext.Request.InputStream);
            }
            catch (Exception exception)
            {
                throw new HttpException(500, exception.Message, exception);
            }
            return xmlData;
        }

        private static string MakeArrayKey(string prefix, int index)
        {
            return prefix + "[" + index.ToString(CultureInfo.InvariantCulture) + "]";
        }

        private static string MakePropertyKey(string prefix, string propertyName)
        {
            return (String.IsNullOrEmpty(prefix)) ? propertyName : prefix + "." + propertyName;
        }
    }
}